#include "mmu/mmu.h"
#include "spinlock.h"
#include "types.h"
#include "console.h"
#include "memlayout.h"

// 页管理节点
struct page {
    struct page *next;
};

// 页管理器
struct allocator {
    struct spinlock lock;
    struct page *freelist;
};

// kernel.ld中定义，是bss段结束地址
extern char end[];
static struct allocator kmem;

// 释放一页内存
void os_kfree(void *addr)
{
    struct page* page;
    u32 flags;

#if 0
    printk("os_kfree(%p).\n", addr);
#endif

    // 检查地址是否合法
    if(((addr_t)addr % PGSIZE) || (char*)addr < end || V2P(addr) >= PHYSTOP)
        panic("os_kfree");

    page = (struct page*)addr;

    // 将空闲页插入到链表头
    os_spin_lock(&kmem.lock, &flags);
    page->next = kmem.freelist;
    kmem.freelist = page;
    os_spin_unlock(&kmem.lock, flags);
}

// 申请一页内存
void *os_kalloc(void)
{
    struct page* page;
    u32 flags;

    // 从链表头取出空闲页
    os_spin_lock(&kmem.lock, &flags);
    page = kmem.freelist;
    if (page) {
        kmem.freelist = page->next;
    }
    os_spin_unlock(&kmem.lock, flags);

    return (void *)page;
}

static void free_range(void *vstart, void *vend)
{
    char *p;

    p = (char*)PGROUNDUP((addr_t)vstart);
    for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
        os_kfree((void *)p);
}

void os_kmem_init1(void *vstart, void *vend)
{
    printk("kmem(%p - %p) init.\n", vstart, vend);
    os_spin_init(&kmem.lock, "kmem");
    free_range(vstart, vend);
    printk("kmem(%p - %p) init success.\n", vstart, vend);
}

void os_kmem_init2(void *vstart, void *vend)
{
    printk("kmem(%p - %p) init.\n", vstart, vend);
    free_range(vstart, vend);
    printk("kmem(%p - %p) init success.\n", vstart, vend);
}

void kmem_test(void)
{
    void *p1, *p2, *p3, *p4;

    p1 = os_kalloc();
    p2 = os_kalloc();
    os_kfree(p1);
    os_kfree(p2);
    p3 = os_kalloc();
    p4 = os_kalloc();
    os_kfree(p3);
    os_kfree(p4);

    if ((p3 == p2) && (p4 == p1)) {
        printk("kmem alloc & free test success.\n");
    } else {
        printk("kmem alloc & free test fail.\n");
        printk("     p1 = %p\n", p1);
        printk("     p2 = %p\n", p2);
        printk("     p3 = %p\n", p3);
        printk("     p4 = %p\n", p4);
    }
}